// My includes
#include "Network.h"

Server::Server()
{
	peer = RakNetworkFactory::GetRakPeerInterface();

	// Initialize array to store NUM_CLIENTS characters
	characters = new Character*[NUM_CLIENTS-1];
	//Initialize array of characters
	for (int i = 0; i < NUM_CLIENTS;i++)
	{
		characters[i] = new Character();
	}
}

Server::~Server()
{
	RakNetworkFactory::DestroyRakPeerInterface(peer);
}

void Server::Start(void)
{
	SocketDescriptor socketDescriptor;
	socketDescriptor.port=(unsigned short)SERVER_PORT;
	peer->Startup((unsigned short) NUM_CLIENTS,0,&socketDescriptor,1);
	peer->SetMaximumIncomingConnections(NUM_CLIENTS);
	printf("STARTED SERVER\n");
}

unsigned Server::ConnectionCount(void) const
{
	unsigned i,count;
	for (i=0,count=0; i < NUM_CLIENTS;i++)
		if (peer->GetSystemAddressFromIndex(i)!=UNASSIGNED_SYSTEM_ADDRESS)
			count++;
	return count;
}

void Server::Update()
{
	Packet *p = peer->Receive();
	while (p)
	{
		switch (p->data[0])
		{
		case ID_CONNECTION_LOST:
			printf("ID_CONNECTION_LOST TOTAL CONNECTIONS = %i\n", ConnectionCount());
			removePlayer(p);
			break;
		case ID_DISCONNECTION_NOTIFICATION:
			printf("ID_DISCONNECTION_NOTIFICATION TOTAL CONNECTIONS = %i\n", ConnectionCount());
			break;
		case ID_NEW_INCOMING_CONNECTION:
			printf("ID_NEW_INCOMING_CONNECTION TOTAL CONNECTIONS = %i\n", ConnectionCount());
			break;
		case MSG_LOGIN_CHALLENGE:
			printf("MSG_LOGIN_CHALLENGE RECEIVED\n");
			loginChallenge(p);
			break;
		case MSG_DELETE_CHARACTER:
			printf("MSG_DELETE_CHARACTER RECEIVED\n");
			dataBase->deleteCharacter(p);
			break;
		case MSG_CREATE_CHARACTER:
			printf("MSG_CREATE_CHARACTER RECEIVED\n");
			dataBase->createCharacter(p);
			break;
		case MSG_RETRIEVE_CHARACTER_DATA:
			printf("MSG_RETRIEVE_CHARACTER_DATA RECEIVED\n");
			dataBase->getCharacter(p);
			break;
		case MSG_CHAT_MSG:
			printf("MSG_CHAT_MSG RECEIVED\n");
			relayChatMsg(p);
			break;
		case MSG_MOVEMENT_MSG:
			printf("MSG_MOVEMENT_MSG RECEIVED\n");
			networkMovement(p);
			break;
		case MSG_ROTATION_MSG:
			printf("MSG_ROTATION_MSG RECEIVED\n");
			networkRotation(p);
			break;
		default:
			break;
		}
		peer->DeallocatePacket(p);
		p = peer->Receive();
	}
}

void Server::setDataBase(Database* db)
{
	dataBase = db;
}

void Server::loginChallenge(Packet *packet)
{
	BitStream dataStream((unsigned char*)packet->data, packet->length, false);

	MessageID typeId;
	RakString account;
	RakString password;

	dataStream.Read(typeId);
	dataStream.Read(account);
	dataStream.Read(password);

	MessageID outTypeId;
	BitStream data;

	if(dataBase->checkAccount(account, password))
	{
		// Account found
		printf("ACCOUNT %s FOUND\n", account.C_String());
		
		// Let client know the account is valid
		outTypeId = MSG_ACCOUNT_VALID;
		data.Reset();
		data.Write(outTypeId);
		peer->Send(&data, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false);

		// Retrieve the character list
		outTypeId = MSG_RETRIEVING_CHARACTER_LIST;
		data.Reset();
		data.Write(outTypeId);
		peer->Send(&data, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false);

		dataBase->retrieveCharacters(account, packet);
	}
	else
	{
		// account not found
		printf("ACCOUNT %s NOT FOUND\n", account.C_String());

		// Let the client know the account is invalid
		outTypeId = MSG_ACCOUNT_INVALID;
		data.Reset();
		data.Write(outTypeId);
		peer->Send(&data, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false);
	}
}

void Server::relayChatMsg(Packet *packet)
{
	BitStream dataStream((unsigned char*)packet->data, packet->length, false);
	peer->Send(&dataStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, UNASSIGNED_SYSTEM_ADDRESS, true);
}

void Server::addPlayer(PlayerData playerData)
{
	//Add player being sent to client to array of characters
	//Loop until entire character array has been checked, size of array = num_clients
	for (int i = 0; i < NUM_CLIENTS;i++)
	{
		// Empty slot in array detected, add character information here
		if (characters[i]->getIp()==UNASSIGNED_SYSTEM_ADDRESS)
		{
			// If slot is available in array add a new player in this position
			characters[i] = new Character(playerData);
			// Send the player to all the clients exept to the one we already sent it to
			MessageID typeId = MSG_NEW_CHARACTER;
			BitStream dataStream;

			dataStream.Write(typeId);
			dataStream.Write(playerData.mId);
			dataStream.Write(playerData.mAccount);
			dataStream.Write(playerData.mName);
			dataStream.Write(playerData.mLevel);
			dataStream.Write(playerData.mClass);
			dataStream.Write(playerData.mStartPosition.x);
			dataStream.Write(playerData.mStartPosition.y);
			dataStream.Write(playerData.mStartPosition.z);
			dataStream.Write(playerData.mStartRotation);
			dataStream.Write(playerData.mModel);
			dataStream.Write(playerData.mGender);
			dataStream.Write(playerData.mIp);

			peer->Send(&dataStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, playerData.mIp, true);
			break;
		}
	}
	// Send all of the current connected characters back to the client
	//Loop until entire player array has been checked, size of array = num_clients
	for (int i = 0; i < NUM_CLIENTS;i++)
	{
		// Character detected, send to client
		if (characters[i]->getIp()!=UNASSIGNED_SYSTEM_ADDRESS && characters[i]->getIp()!=playerData.mIp)
		{
			// Send the player to the client
			MessageID typeId = MSG_NEW_CHARACTER;
			BitStream dataStream;

			RakString name = characters[i]->getName().c_str();
			RakString model = characters[i]->getModel().c_str();

			dataStream.Write(typeId);
			dataStream.Write(characters[i]->getId());
			dataStream.Write(characters[i]->getAccount());
			dataStream.Write(name);
			dataStream.Write(characters[i]->getLevel());
			dataStream.Write(characters[i]->getClass());
			dataStream.Write(characters[i]->getStartPosition().x);
			dataStream.Write(characters[i]->getStartPosition().y);
			dataStream.Write(characters[i]->getStartPosition().z);
			dataStream.Write(characters[i]->getStartRotation());
			dataStream.Write(model);
			dataStream.Write(characters[i]->getGender());
			dataStream.Write(characters[i]->getIp());

			peer->Send(&dataStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, playerData.mIp, false);
		}
	}
}

// remove player
void Server::removePlayer(Packet *packet)
{
	for(int i = 0; i < NUM_CLIENTS; i++)
	{
		if(characters[i]->getIp()==packet->systemAddress)
		{
			printf("Player %s has left\n", characters[i]->getName().c_str());

			/*dataBase->savePlayer(players[i]);*/ // TO IMPLEMENT

			// Let all the clients know that a player left
			BitStream dataStream;
			MessageID typeId;
			typeId = MSG_CHARACTER_LEFT;

			dataStream.Write(typeId);
			dataStream.Write(characters[i]->getId());

			peer->Send(&dataStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, true);

			// Reinitialize empty spot
			characters[i] = new Character();

			break;
		}
	}
}

// Handles network movement
void Server::networkMovement(Packet *packet)
{
	// NEED TO IMPLEMENT SERVER STORE POSITION

	// Relay data to other clients
	BitStream dataStream((unsigned char*)packet->data, packet->length, false);
	peer->Send(&dataStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, true);
}

void Server::networkRotation(Packet *packet)
{
	// NEED TO IMPLEMENT SERVER STORE ROTATION

	// Relay data to other clients
	BitStream dataStream((unsigned char*)packet->data, packet->length, false);
	peer->Send(&dataStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, true);
}